Homework: boot xv6

boot XV6

首先搭建相应的实现环境,具体可以参考实验环境搭建

按照下面的指令进行clone和编译:

Fetch the xv6 source:
$ mkdir 6.828
$ cd 6.828
$ git clone git://github.com/mit-pdos/xv6-public.git

Build xv6 on Athena:
$ cd xv6-public
$ make

Finding and breaking at an address

​ 这一部分就是让我们找到相关的函数的地址,并且在这个地址打上断点,便于之后的调试。

​ 这里以内核的启动入口为例,首先我们使用nm1工具找到内核的入口0x0010000c,之后在这个地址打上断点br * 0x0010000c,之后就能通过gdb的si指令进行单步的调试了。

​ 关于这个内核的启动地址为什么是0x100000c,这个在kernel.ld固定设置,并且是一个不成文的规定。

# 一个终端:
make qemu-gdb
# 另外一个终端:
make gdb

Image

Exercise: What is on the stack?

​ 本部分希望我们了解函数调用时栈发生了什么变化。

Q1:什么时候初始化了栈空间?

bootasm.S:   movl    $start, %esp # $start==0x7c00
 260x7c43:      mov    $0x7c00,%sp
 270x7c46:      add    %al,(%bx,%si)
 28├──> 0x7c48:      call   0x7d39
 290x7c4b:      add    %al,(%bx,%si)
 300x7c4d:      mov    $0x89668a00,%eax
 310x7c53:      ret    $0xef66
 320x7c56:      mov    $0xef668ae0,%eax
 330x7c5c:      jmp    0x7c5c
 340x7c5e:      xchg   %eax,%eax
 350x7c60:      add    %al,(%bx,%si)
 360x7c62:      add    %al,(%bx,%si)
 370x7c64:      add    %al,(%bx,%si)
 380x7c66:      add    %al,(%bx,%si)
 390x7c68:      (bad)
 400x7c69:      incw   (%bx,%si)
 410x7c6b:      add    %al,(%bx,%si)
**    0x7c00:   cli     (7c00 - 7cea) **                                        
gs             0x0      0
(gdb) b *0x7c48
Breakpoint 2 at 0x7c48
(gdb) c
Continuing.
The target architecture is assumed to be i386
=> 0x7c48:      call   0x7d3b

Breakpoint 2, 0x00007c48 in ?? ()
(gdb) info reg
eax            0x0      0
ecx            0x0      0
edx            0x80     128
ebx            0x0      0
esp            0x7c00   0x7c00
ebp            0x0      0x0
esi            0x0      0
edi            0x0      0
eip            0x7c48   0x7c48
eflags         0x6      [ PF ]
cs             0x8      8
ss             0x10     16
ds             0x10     16
es             0x10     16
fs             0x0      0
gs             0x0      0
(gdb)

Q2: 单步执行到bootmain,看看栈发生了什么?

  call    bootmain
    7c48:    e8 ee 00 00 00           call   7d3b <bootmain>

此刻esp = 0x7c00变化为esp = 0x7bfc,并且里面保存的值为0x7c4d,连保存的是call bootmain的后面一个语句的执行地址。

之后是一系列的压栈操作:

    7d3b:    55                       push   %ebp
    7d3c:    89 e5                    mov    %esp,%ebp
    7d3e:    57                       push   %edi
    7d3f:    56                       push   %esi
    7d40:    53                       push   %ebx
    7d41:    83 ec 0c                 sub    $0xc,%esp

此时esp = 0x7be0, ebp=0x7bf8。

Q3: 第一个操作栈的指令是什么?

    7d3b:    55                       push   %ebp
    7d3c:    89 e5                    mov    %esp,%ebp

可以看到是对原来的ebp进行保存,并且将原来的esp当作新的函数调用的栈底。

Q4:调用到0x10000c之前,栈寄存器发生了什么?

我们主要理解栈里面填充的内容是哪些指令来的。

之后我们在调用kernel入口函数之前打上一个断点:

Image

发现期间函数栈没有发生变化。

执行完call指令之后,发现还是相同的套路,esp = esp-4,并且保存的值是call 指令后面的那个地址,即 0x7db4

Image

红框中的值是两个函数调用的返回地址值,期间的若干值是下面的指令产生的:

    7d3b:    55                       push   %ebp
    7d3c:    89 e5                    mov    %esp,%ebp
    7d3e:    57                       push   %edi
    7d3f:    56                       push   %esi
    7d40:    53                       push   %ebx
    7d41:    83 ec 0c                 sub    $0xc,%esp
    ...

    7d82:    ff 73 04                 pushl  0x4(%ebx)
    7d85:    ff 73 10                 pushl  0x10(%ebx)
    7d88:    57                       push   %edi

正好是7个push的指令。

1. nm - list symbols from object files

results matching ""

    No results matching ""